package com.github.data_manage.controller

import com.github.data_manage.common.CommonResult
import com.github.data_manage.config.SystemException
import com.github.data_manage.dao.ContrastBusinessDataDao
import com.github.data_manage.dao.ContrastDao
import com.github.data_manage.dao.FundBusinessDataDao
import com.github.data_manage.dao.FundDao
import com.github.data_manage.dao.FundDepthResearchAttachAnnualInfoDao
import com.github.data_manage.dao.FundDepthResearchAttachDao
import com.github.data_manage.dao.FundDepthResearchAttachQuarterlyInfoDao
import com.github.data_manage.dao.FundDepthResearchContrastAttachAnnualInfoDao
import com.github.data_manage.dao.FundDepthResearchContrastAttachDao
import com.github.data_manage.dao.FundDepthResearchContrastAttachQuarterlyInfoDao
import com.github.data_manage.dao.FundDepthResearchDao
import com.github.data_manage.dao.FundNavDataDao
import com.github.data_manage.dao.FundReturnDataDao
import com.github.data_manage.domain.po.ExposureInfo
import com.github.data_manage.domain.po.Fund
import com.github.data_manage.domain.vo.AnnualPnLAttributionByAssetClassVo
import com.github.data_manage.domain.vo.AnnualPnLAttributionByCountryVo
import com.github.data_manage.domain.vo.AnnualPnLAttributionByMarketCapVo
import com.github.data_manage.domain.vo.AnnualPnLAttributionBySectorVo
import com.github.data_manage.domain.vo.BatchFundContrastDataQueryVo
import com.github.data_manage.domain.vo.ConcentrationOfTopPositionsOneVo
import com.github.data_manage.domain.vo.ConcentrationOfTopPositionsTwoVo
import com.github.data_manage.domain.vo.Contrast3M6MOutUnderPerfVo
import com.github.data_manage.domain.vo.Contrast12M36MOutUnderPerfVo
import com.github.data_manage.domain.vo.ContrastRollBetaAndCorrelVo
import com.github.data_manage.domain.vo.ContrastRollPerfAndVolVo
import com.github.data_manage.domain.vo.ContrastValueVo
import com.github.data_manage.domain.vo.CumulativePnLAttributionByAssetClassVo
import com.github.data_manage.domain.vo.CumulativePnLAttributionByCountryVo
import com.github.data_manage.domain.vo.CumulativePnLAttributionByMarketCapVo
import com.github.data_manage.domain.vo.CumulativePnLAttributionBySectorVo
import com.github.data_manage.domain.vo.FirmStrategyFundAumVo
import com.github.data_manage.domain.vo.FundAumCumulativeReturnVo
import com.github.data_manage.domain.vo.FundAumMonthlyFlowVo
import com.github.data_manage.domain.vo.FundValueVo
import com.github.data_manage.domain.vo.GrossExposureCumulativePerformanceVo
import com.github.data_manage.domain.vo.IntTimeDataVo
import com.github.data_manage.domain.vo.LargestLongPositionsVo
import com.github.data_manage.domain.vo.LargestShortPositionsVo
import com.github.data_manage.domain.vo.LongBookAnnualAlphaBetaVo
import com.github.data_manage.domain.vo.LongBookQuarterlyAlphaBetaVo
import com.github.data_manage.domain.vo.LongShortExposureByAssetClassVo
import com.github.data_manage.domain.vo.LongShortExposureCumulativePerformanceVo
import com.github.data_manage.domain.vo.NetExposureCumulativePerformanceVo
import com.github.data_manage.domain.vo.NumberOfPositionsGrossExposureVo
import com.github.data_manage.domain.vo.OverallBookAnnualAlphaBetaVo
import com.github.data_manage.domain.vo.OverallBookAnnualLongShortVo
import com.github.data_manage.domain.vo.OverallBookQuarterlyAlphaBetaVo
import com.github.data_manage.domain.vo.OverallBookQuarterlyLongShortVo
import com.github.data_manage.domain.vo.PerformanceAnalysisIndicesVo
import com.github.data_manage.domain.vo.PerformanceOverDifferentStressPeriodVo
import com.github.data_manage.domain.vo.ShortBookAnnualAlphaBetaVo
import com.github.data_manage.domain.vo.ShortBookQuarterlyAlphaBetaVo
import com.github.data_manage.domain.vo.ShortExposureCumulativePerformanceVo
import com.github.data_manage.domain.vo.StrategyAumCumulativeReturnVo
import com.github.data_manage.domain.vo.StrategyAumMonthlyVo
import com.github.data_manage.domain.vo.StringTimeDataVo
import com.github.data_manage.domain.vo.ThirtySixRollingPerformanceMetricsVo
import com.github.data_manage.domain.vo.TimeDataVo
import com.github.data_manage.domain.vo.TopAnnualPnlDriversVo
import com.github.data_manage.domain.vo.TopMonthlyPnlLosersVo
import com.github.data_manage.domain.vo.TopMonthlyPnlWinnersVo
import com.github.data_manage.domain.vo.TwelveRollingPerformanceMetricsVo
import com.github.data_manage.service.ContrastService
import com.github.data_manage.service.FundDepthResearchAttachService
import com.github.data_manage.util.DataUtils
import com.github.data_manage.util.DataUtils.standardDeviation
import com.github.data_manage.util.DataUtils.ytd
import com.github.data_manage.util.DateTimeUtils
import com.github.data_manage.util.DateTimeUtils.Companion.atEndOfMonth
import com.github.data_manage.util.DateTimeUtils.Companion.quarter
import com.github.data_manage.util.consumeAsyncBlocking
import com.github.data_manage.util.mapAsync
import com.github.data_manage.util.mapAsyncBlocking
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import org.ehcache.impl.internal.concurrent.ConcurrentHashMap
import org.slf4j.LoggerFactory
import org.springframework.data.repository.findByIdOrNull
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.LocalDate
import kotlin.collections.first
import kotlin.collections.set

/**
 *
 * @author chenhainan
 * @since 2025/8/19
 */
@RestController
@Tag(name = "FundChartController", description = "基金图表接口")
@RequestMapping("/fundChart")
class FundChartController(
    private val fundDao: FundDao,
    private val contrastDao: ContrastDao,
    private val fundDepthResearchDao: FundDepthResearchDao,
    private val fundDepthResearchAttachDao: FundDepthResearchAttachDao,
    private val fundDepthResearchContrastAttachDao: FundDepthResearchContrastAttachDao,
    private val fundDepthResearchContrastAttachAnnualInfoDao: FundDepthResearchContrastAttachAnnualInfoDao,
    private val fundDepthResearchContrastAttachQuarterlyInfoDao: FundDepthResearchContrastAttachQuarterlyInfoDao,
    private val fundDepthResearchAttachAnnualInfoDao: FundDepthResearchAttachAnnualInfoDao,
    private val fundDepthResearchAttachQuarterlyInfoDao: FundDepthResearchAttachQuarterlyInfoDao,
    private val fundNavDataDao: FundNavDataDao,
    private val fundReturnDataDao: FundReturnDataDao,
    private val fundBusinessDataDao: FundBusinessDataDao,
    private val contrastBusinessDataDao: ContrastBusinessDataDao,
    private val contrastService: ContrastService,
    private val fundDepthResearchAttachService: FundDepthResearchAttachService,
) {

    companion object {

        private val log = LoggerFactory.getLogger(FundChartController::class.java)
    }


    @GetMapping("/firmStrategyFundAum")
    @Operation(description = "图表1-2")
    fun firmStrategyFundAum(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<FirmStrategyFundAumVo>>> {
        val results = fundDepthResearchAttachDao.findAllByFundIdIs(fundId)
            .map { TimeDataVo(it.dataMonth, FirmStrategyFundAumVo(it.fundAumMn, it.strategyAumMn, it.firmAumMn)) }
            .sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("strategyAumCumulativeReturn")
    @Operation(description = "图表3")
    fun strategyAumCumulativeReturn(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<StrategyAumCumulativeReturnVo>>> {
        val fundDepthResearchAttaches = fundDepthResearchAttachDao.findAllByFundIdIs(fundId)
        if (fundDepthResearchAttaches.isEmpty()) {
            return CommonResult.success(emptyList())
        }
        val startTime = fundDepthResearchAttaches.last().dataMonth
        val endTime = fundDepthResearchAttaches.first().dataMonth
        val allFundNavData =
            fundNavDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, endTime).groupBy { it.time!! }
        val results = fundDepthResearchAttaches.map {
            TimeDataVo(
                it.dataMonth, StrategyAumCumulativeReturnVo(
                    it.strategyAumMn,
                    it.changeInStrategyAumDueToInflowAppro,
                    allFundNavData[it.dataMonth]?.first()?.value
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("strategyAumMonthlyFlow")
    @Operation(description = "图表4")
    fun strategyAumMonthly(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<StrategyAumMonthlyVo>>> {
        val results = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map {
            TimeDataVo(
                it.dataMonth,
                StrategyAumMonthlyVo(
                    it.strategyAumMn,
                    it.changeInStrategyAumDueToPerformance,
                    it.changeInStrategyAumDueToInflowAppro
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("fundAumCumulativeReturn")
    @Operation(description = "图表5")
    fun fundAumCumulativeReturn(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<FundAumCumulativeReturnVo>>> {
        val fundDepthResearchAttaches = fundDepthResearchAttachDao.findAllByFundIdIs(fundId)
        if (fundDepthResearchAttaches.isEmpty()) {
            return CommonResult.success(emptyList())
        }
        val startTime = fundDepthResearchAttaches.last().dataMonth
        val endTime = fundDepthResearchAttaches.first().dataMonth
        val allFundNavData =
            fundNavDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, endTime).groupBy { it.time!! }
        val results = fundDepthResearchAttaches.map {
            TimeDataVo(
                it.dataMonth, FundAumCumulativeReturnVo(
                    it.fundAumMn,
                    it.changeInStrategyAumDueToInflowAppro,
                    allFundNavData[it.dataMonth]?.first()?.value
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("fundAumMonthlyFlow")
    @Operation(description = "图表6")
    fun fundAumMonthlyFlow(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<FundAumMonthlyFlowVo>>> {
        val results = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map {
            TimeDataVo(
                it.dataMonth,
                FundAumMonthlyFlowVo(
                    it.fundAumMn,
                    it.changeInStrategyAumDueToPerformance,
                    it.changeInStrategyAumDueToInflowAppro
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @PostMapping("performanceAnalysisIndices")
    @Operation(description = "图表 7")
    fun performanceAnalysisIndices(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<Collection<TimeDataVo<PerformanceAnalysisIndicesVo>>> {
        val fund = fundDao.findById(batchQueryVo.baseFundId).orElseThrow { SystemException("未找到基金信息") }
        val fundNavDataList = fundNavDataDao.findAllByFundId(batchQueryVo.baseFundId)
        if (fundNavDataList.isEmpty()) {
            return CommonResult.success(emptyList())
        }
        val resultMap = ConcurrentHashMap<LocalDate, TimeDataVo<PerformanceAnalysisIndicesVo>>(fundNavDataList.size)
        fundNavDataList.forEach { fundNavData ->
            resultMap[fundNavData.time!!] =
                TimeDataVo(
                    fundNavData.time!!,
                    PerformanceAnalysisIndicesVo(
                        FundValueVo(fund.fundId!!, fund.fundName!!, fundNavData.value),
                        mutableListOf()
                    )
                )
        }
        val endTime = fundNavDataList.first().time!!
        val startTime = fundNavDataList.last().time!!
        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            val contrastAllBusinessData = contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                contrastId,
                startTime,
                endTime
            )
            for (contrastNavData in contrastService.calcContrastNavData(contrast, contrastAllBusinessData)) {
                resultMap[contrastNavData.time!!]?.value?.contrastReturnInfos?.add(
                    ContrastValueVo(
                        contrastId,
                        contrast.name,
                        contrastNavData.value
                    )
                )
            }
        }
        val results = resultMap.values.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @PostMapping("drawdownAnalysisIndices")
    @Operation(description = "图表 8")
    fun drawdownAnalysisIndices(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<Collection<TimeDataVo<PerformanceAnalysisIndicesVo>>> {
        val fund = fundDao.findById(batchQueryVo.baseFundId).orElseThrow { SystemException("未找到基金信息") }
        val fundReturnDataList = fundReturnDataDao.findAllByFundId(batchQueryVo.baseFundId)
        if (fundReturnDataList.isEmpty()) {
            return CommonResult.success(emptyList())
        }
        val resultMap = ConcurrentHashMap<LocalDate, TimeDataVo<PerformanceAnalysisIndicesVo>>(fundReturnDataList.size)
        fundReturnDataList.forEach { fundReturnData ->
            resultMap[fundReturnData.time!!] =
                TimeDataVo(
                    fundReturnData.time!!,
                    PerformanceAnalysisIndicesVo(
                        FundValueVo(fund.fundId!!, fund.fundName!!, fundReturnData.value),
                        mutableListOf()
                    )
                )
        }
        val endTime = fundReturnDataList.last().time!!
        val startTime = fundReturnDataList.first().time!!
        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            val contrastAllBusinessData = contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                contrastId,
                startTime,
                endTime
            )
            for (contrastReturnData in contrastService.calcContrastReturnData(contrast, contrastAllBusinessData)) {
                resultMap[contrastReturnData.time!!]?.value?.contrastReturnInfos?.add(
                    ContrastValueVo(
                        contrastId,
                        contrast.name,
                        contrastReturnData.value
                    )
                )
            }
        }
        val results = resultMap.values.sortedBy { it.time }
        return CommonResult.success(results)
    }


    @PostMapping("performanceOverDifferentStressPeriod")
    @Operation(description = "图表9")
    fun performanceOverDifferentStressPeriod(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo):
            CommonResult<List<StringTimeDataVo<PerformanceOverDifferentStressPeriodVo>>> = runBlocking {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }

        fun performanceOverDifferentStressPeriod(
            startTime: LocalDate,
            endTime: LocalDate,
            fund: Fund,
            contrastIds: List<Int>,
            timeRangeName: String
        ): StringTimeDataVo<PerformanceOverDifferentStressPeriodVo> =
            StringTimeDataVo(
                timeRangeName,
                startTime,
                PerformanceOverDifferentStressPeriodVo(
                    FundValueVo(
                        fund.fundId!!, fund.fundName!!,
                        fundBusinessDataDao.findAllByFundIdAndTimeBetween(
                            fund.fundId!!,
                            startTime,
                            endTime
                        ).map { it.value }.ytd(true) ?: BigDecimal.ZERO
                    ),
                    contrastIds.map { contrastId ->
                        val contrast = contrastDao.findByIdOrNull(contrastId)!!

                        ContrastValueVo(
                            contrastId,
                            contrast.name,
                            contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startTime, endTime)
                                .map { it.value }.ytd(true) ?: BigDecimal.ZERO
                        )
                    }
                )
            )


        val results = awaitAll(
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2007, 11, 30),
                    LocalDate.of(2008, 7, 31),
                    fund,
                    batchQueryVo.contrastIds,
                    "Subprime Crisis (Nov 07 - Jul 08)"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2008, 8, 31),
                    LocalDate.of(2008, 10, 31),
                    fund,
                    batchQueryVo.contrastIds,
                    "Credit Meltdown (Aug 08 - Oct 08）"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2007, 11, 30),
                    LocalDate.of(2008, 7, 31),
                    fund,
                    batchQueryVo.contrastIds,
                    "Subprime Crisis (Nov 07 - Jul 08)"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2010, 5, 31), LocalDate.of(2010, 6, 30),
                    fund,
                    batchQueryVo.contrastIds,
                    "Greece Debt Crisis (May 10 - Jun   10)"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2011, 5, 31), LocalDate.of(2011, 9, 30),
                    fund,
                    batchQueryVo.contrastIds,
                    "US/Europe Crisis (May 11 - Sep 11)"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2012, 3, 31), LocalDate.of(2012, 5, 31),
                    fund,
                    batchQueryVo.contrastIds,
                    "Euro Sovereign Debt Crisis II (Mar   12 - May 12)"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2015, 5, 31), LocalDate.of(2015, 9, 30),
                    fund,
                    batchQueryVo.contrastIds,
                    "RMB Devaluation Shock (May 15 -   Sep 15)"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2016, 1, 31),
                    LocalDate.of(2016, 2, 1).atEndOfMonth(),
                    fund,
                    batchQueryVo.contrastIds,
                    "China Circuit Breaker Shock (Jan   16 - Feb 16)"
                )
            },
            async {
                performanceOverDifferentStressPeriod(
                    LocalDate.of(2020, 3, 1), LocalDate.of(2020, 3, 31),
                    fund,
                    batchQueryVo.contrastIds,
                    "Covid-19 ( Mar 2020)"
                )
            }
        ).sortedBy { it.date }
        return@runBlocking CommonResult.success(results)
    }



    @PostMapping("twelveRollingPerformanceMetricsVo")
    @Operation(description = "图表10")
    fun twelveRollingPerformanceMetricsVo(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<TwelveRollingPerformanceMetricsVo>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val (startTime, endTime) =
            fundDepthResearchAttachService.findFundDepthResearchTimeRange(fundId) ?: return CommonResult.success(
                emptyList()
            )
        if (startTime == null || endTime == null) {
            return CommonResult.success(
                emptyList(), message = "无数据"
            )
        }

        val fundBusinessDataMap =
            fundBusinessDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, endTime)
                .sortedBy { it.time }
                .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })

        val results = runBlocking {
            val fundValueTask = async {
                val fundValueMap = HashMap<LocalDate, Pair<BigDecimal?, BigDecimal?>>()
                var index = 0L
                val fundBusinessDataMap =
                    fundBusinessDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, endTime)
                        .sortedBy { it.time }
                        .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
                do {
                    val currentMonth = startTime.plusMonths(index).atEndOfMonth()
                    val fundMonthValues = (0..11L).mapNotNull { i ->
                        fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first()
                    }.reversed()
                    val fundMRollPerf = if (index < 11) {
                        null
                    } else {
                        fundMonthValues.ytd() ?: BigDecimal.ZERO
                    }
                    val fundMRollVol = if (index < 11) {
                        null
                    } else {
                        fundMonthValues.standardDeviation()
                    }
                    index++
                    fundValueMap[currentMonth] = Pair(fundMRollPerf, fundMRollVol)
                } while (!(currentMonth.year == endTime.year && currentMonth.month == endTime.month))
                fundValueMap
            }
            val contrastValueMap =
                ConcurrentHashMap<LocalDate, MutableList<ContrastRollPerfAndVolVo>>(fundBusinessDataMap.size * 2)
            batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
                val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
                var index = 0L
                val contrastBusinessDataMap =
                    contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startTime, endTime)
                        .sortedBy { it.time }
                        .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })

                do {
                    val currentMonth = startTime.plusMonths(index).atEndOfMonth()

                    val contrastMonthValues = (0..11L).mapNotNull { i ->
                        contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.reversed()
                    val contrastRollBeta = if (index < 11) {
                        null
                    } else {
                        contrastMonthValues.ytd() ?: BigDecimal.ZERO
                    }
                    val contrastRollCorrel = if (index < 11) {
                        null
                    } else {
                        contrastMonthValues.standardDeviation()
                    }
                    index++
                    val list = (contrastValueMap[currentMonth] ?: mutableListOf())
                    list.add(
                        ContrastRollPerfAndVolVo(
                            contrastId,
                            contrast.name,
                            contrastRollBeta,
                            contrastRollCorrel
                        )
                    )
                    contrastValueMap[currentMonth] = list
                } while (!(currentMonth.year == endTime.year && currentMonth.month == endTime.month))
            }
            return@runBlocking fundValueTask.await().entries.map { fundValueEntry ->
                val month = fundValueEntry.key
                val (fundMRollPerf, fundMRollVol) = fundValueEntry.value
                val contrastMonthValues = contrastValueMap[month] ?: emptyList()
                TimeDataVo(
                    month,
                    TwelveRollingPerformanceMetricsVo(
                        fund.fundName!!,
                        fundMRollPerf,
                        fundMRollVol,
                        contrastMonthValues
                    )
                )
            }
        }
        return CommonResult.success(results.sortedBy { it.time })
    }

    @PostMapping("thirtySixRollingPerformanceMetricsVo")
    @Operation(description = "图表11")
    fun thirtySixRollingPerformanceMetricsVo(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<ThirtySixRollingPerformanceMetricsVo>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val (fundDataStartTime, fundDataEndTime) =
            fundDepthResearchAttachService.findFundDepthResearchTimeRange(fundId) ?: return CommonResult.success(
                emptyList()
            )
        if (fundDataStartTime == null || fundDataEndTime == null) {
            return CommonResult.success(
                emptyList(), message = "无数据"
            )
        }

        val mEndTime = fundDataEndTime.plusMonths(36).atEndOfMonth()
        // 从endTime往后面数3年每个月来计算RollPerf与RollVol
        val results = runBlocking {
            val fundValueTask = async {
                val fundValueMap = HashMap<LocalDate, Pair<BigDecimal?, BigDecimal?>>()
                var index = 0L
                val fundBusinessDataMap =
                    fundBusinessDataDao.findAllByFundIdAndTimeBetween(
                        fundId,
                        fundDataStartTime,
                        fundDataEndTime
                    ).sortedBy { it.time }
                        .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
                var zeroCount = 0
                do {
                    val currentMonth = fundDataStartTime.plusMonths(index).atEndOfMonth()

                    index++
                    val fundThirtySixMRollPerf = if (DateTimeUtils.betweenMonth(fundDataStartTime, currentMonth) < 35) {
                        null
                    } else {
                        (0..35L).mapNotNull { i ->
                            fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first()
                        }.ytd() ?: BigDecimal.ZERO
                    }
                    val fundThirtySixMRollVol = if (DateTimeUtils.betweenMonth(fundDataStartTime, currentMonth) < 35) {
                        null
                    } else {
                        (0..35L).mapNotNull { i ->
                            fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first()
                        }.standardDeviation()
                    }
                    fundValueMap[currentMonth] = Pair(fundThirtySixMRollPerf, fundThirtySixMRollVol)
                } while (!(currentMonth.year == mEndTime.year && currentMonth.month == mEndTime.month))
                fundValueMap
            }
            val contrastValueMap =
                batchQueryVo.contrastIds.mapAsync { contrastId ->
                    val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@mapAsync null
                    val contrastValueMap = HashMap<LocalDate, ContrastRollPerfAndVolVo>()
                    var index = 0L
                    val contrastBusinessDataMap =
                        contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                            contrastId,
                            fundDataStartTime,
                            fundDataEndTime
                        ).sortedBy { it.time }
                            .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
                    do {
                        val currentMonth = fundDataStartTime.plusMonths(index).atEndOfMonth()
                        index++
                        val contrastThirtySixMRollPerf =
                            if (DateTimeUtils.betweenMonth(fundDataStartTime, currentMonth) < 35) {
                                null
                            } else {
                                (0..35L).mapNotNull { i ->
                                    contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first()
                                }.ytd() ?: BigDecimal.ZERO
                            }
                        val contrastThirtySixMRollVol =
                            if (DateTimeUtils.betweenMonth(fundDataStartTime, currentMonth) < 35) {
                                null
                            } else {
                                (0..35L).mapNotNull { i ->
                                    contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first()
                                }.standardDeviation()
                            }
                        contrastValueMap[currentMonth] = ContrastRollPerfAndVolVo(
                            contrastId,
                            contrast.name,
                            contrastThirtySixMRollPerf,
                            contrastThirtySixMRollVol
                        )
                    } while (!(currentMonth.year == mEndTime.year && currentMonth.month == mEndTime.month))
                    return@mapAsync contrastValueMap
                }.awaitAll().filterNotNull()
                    .flatMap { it.entries }
                    .groupBy({ it.key }, { it.value })
                    .mapValues { (_, values) -> values.toList() }
            return@runBlocking fundValueTask.await().entries
                .map { entry ->
                    TimeDataVo(
                        entry.key,
                        ThirtySixRollingPerformanceMetricsVo(
                            fund.fundName!!,
                            entry.value.first,
                            entry.value.second,
                            contrastValueMap[entry.key] ?: emptyList()
                        )
                    )
                }
        }
        return CommonResult.success(results.sortedBy { it.time })
    }

    @PostMapping("twelveRollingPerformanceMetricsBetaAndCorrel")
    @Operation(description = "图表12")
    fun twelveRollingPerformanceMetricsBetaAndCorrel(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<MutableList<ContrastRollBetaAndCorrelVo>>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val (fundDataStartTime, fundDataEndTime) =
            fundDepthResearchAttachService.findFundDepthResearchTimeRange(fundId) ?: return CommonResult.success(
                emptyList()
            )
        if (fundDataStartTime == null || fundDataEndTime == null) {
            return CommonResult.success(
                emptyList(), message = "无数据"
            )
        }
        val fundBusinessDataMap =
            fundBusinessDataDao.findAllByFundIdAndTimeBetween(fundId, fundDataStartTime, fundDataEndTime)
                .sortedBy { it.time }
                .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
        val resultMap =
            ConcurrentHashMap<LocalDate, MutableList<ContrastRollBetaAndCorrelVo>>(fundBusinessDataMap.size * 2)
        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            var index = 0L
            val contrastBusinessDataMap =
                contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                    contrastId,
                    fundDataStartTime,
                    fundDataEndTime
                )
                    .sortedBy { it.time }
                    .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
            do {
                val currentMonth = fundDataStartTime.plusMonths(index).atEndOfMonth()

                val fundLastThirtySixMonthValues = (0..11L).map { i ->
                    fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                }.reversed()
                val contrastLastThirtySixMonthValues = (0..11L).map { i ->
                    contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                }.reversed()
                val contrastRollBeta = if (index < 11) {
                    null
                } else {
                    DataUtils.slope(
                        contrastLastThirtySixMonthValues,
                        fundLastThirtySixMonthValues,
                    )?.multiply(BigDecimal.valueOf(100))
                }
                val contrastRollCorrel = if (index < 11) {
                    null
                } else {
                    DataUtils.pearsonCorrelation(
                        contrastLastThirtySixMonthValues,
                        fundLastThirtySixMonthValues,

                        )?.multiply(BigDecimal.valueOf(100))
                }
                index++
                val list = (resultMap[currentMonth] ?: mutableListOf())
                list.add(
                    ContrastRollBetaAndCorrelVo(
                        contrastId,
                        contrast.name,
                        contrastRollBeta,
                        contrastRollCorrel
                    )
                )
                resultMap[currentMonth] = list
            } while (!(currentMonth.year == fundDataEndTime.year && currentMonth.month == fundDataEndTime.month))
        }


        return CommonResult.success(resultMap.entries.map {
            TimeDataVo(
                it.key,
                it.value
            )
        }.sortedBy { it.time })
    }


    @PostMapping("thirtySixRollingPerformanceMetricsBetaAndCorrel")
    @Operation(description = "图表13")
    fun thirtySixRollingPerformanceMetricsBetaAndCorrel(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<MutableList<ContrastRollBetaAndCorrelVo>>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val (fundDataStartTime, fundDataEndTime) =
            fundDepthResearchAttachService.findFundDepthResearchTimeRange(fundId) ?: return CommonResult.success(
                emptyList()
            )
        if (fundDataStartTime == null || fundDataEndTime == null) {
            return CommonResult.success(
                emptyList(), message = "无数据"
            )
        }
        val mEndTime = fundDataEndTime.plusMonths(36).atEndOfMonth()
        val fundBusinessDataMap =
            fundBusinessDataDao.findAllByFundIdAndTimeBetween(fundId, fundDataStartTime, fundDataEndTime)
                .sortedBy { it.time }
                .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
        val resultMap =
            ConcurrentHashMap<LocalDate, MutableList<ContrastRollBetaAndCorrelVo>>(fundBusinessDataMap.size * 2)
        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            var index = 0L
            val contrastBusinessDataMap =
                contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                    contrastId,
                    fundDataStartTime,
                    fundDataEndTime
                )
                    .sortedBy { it.time }
                    .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
            do {
                val currentMonth = fundDataStartTime.plusMonths(index).atEndOfMonth()

                val fundLastThirtySixMonthValues = (0..35L).map { i ->
                    fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                }.reversed()
                val contrastLastThirtySixMonthValues = (0..35L).map { i ->
                    contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                }.reversed()
                val thirtySixMRollBeta = if (index < 35) {
                    null
                } else {
                    DataUtils.slope(
                        contrastLastThirtySixMonthValues,
                        fundLastThirtySixMonthValues,
                    )?.multiply(BigDecimal.valueOf(100))
                }
                val thirtySixMRollCorrel = if (index < 35) {
                    null
                } else {
                    DataUtils.pearsonCorrelation(
                        contrastLastThirtySixMonthValues,
                        fundLastThirtySixMonthValues
                    )?.multiply(BigDecimal.valueOf(100))
                }
                index++
                val list = (resultMap[currentMonth] ?: mutableListOf())
                list.add(
                    ContrastRollBetaAndCorrelVo(
                        contrastId,
                        contrast.name,
                        thirtySixMRollBeta,
                        thirtySixMRollCorrel
                    )
                )
                resultMap[currentMonth] = list
            } while (!(currentMonth.year == mEndTime.year && currentMonth.month == mEndTime.month))
        }


        return CommonResult.success(resultMap.entries.map {
            TimeDataVo(
                it.key,
                it.value
            )
        }.sortedBy { it.time })
    }

    @PostMapping("contrast3M6MOutUnderPerf")
    @Operation(description = "图表14")
    fun contrast3M6MOutUnderPerf(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<MutableList<Contrast3M6MOutUnderPerfVo>>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val (startTime, fundDataEndTime) =
            fundDepthResearchAttachService.findFundDepthResearchTimeRange(fundId) ?: return CommonResult.success(
                emptyList()
            )
        if (startTime == null || fundDataEndTime == null) {
            return CommonResult.success(
                emptyList(), message = "无数据"
            )
        }
        val fundBusinessDataMap =
            fundBusinessDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, fundDataEndTime)
                .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
        val resultMap =
            ConcurrentHashMap<LocalDate, MutableList<Contrast3M6MOutUnderPerfVo>>(fundBusinessDataMap.size * 2)

        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            var index = 0L
            val contrastBusinessDataMap: Map<LocalDate, List<BigDecimal>> =
                contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startTime, fundDataEndTime)
                    .groupBy({ it.time!! }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
            do {
                val currentMonth = startTime.plusMonths(index).atEndOfMonth()

                val threeMOutUnderPerf = if (index < 2 || currentMonth > fundDataEndTime) {
                    null
                } else {
                    val fundMRollPerf = (0..2L).map { i ->
                        fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    val contrastMRollPerf = (0..2L).map { i ->
                        contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    fundMRollPerf?.subtract(contrastMRollPerf)
                }
                val sixMOutUnderPerf = if (index < 5 || currentMonth > fundDataEndTime) {
                    null
                } else {
                    val fundMRollPerf = (0..5L).map { i ->
                        fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    val contrastMRollPerf = (0..5L).map { i ->
                        contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    fundMRollPerf?.subtract(contrastMRollPerf)
                }
                index++
                val list = (resultMap[currentMonth] ?: mutableListOf())
                list.add(
                    Contrast3M6MOutUnderPerfVo(
                        contrastId,
                        contrast.name,
                        threeMOutUnderPerf,
                        sixMOutUnderPerf,
                    )
                )
                resultMap[currentMonth] = list
            } while (!(currentMonth.year == fundDataEndTime.year && currentMonth.month == fundDataEndTime.month))
        }

        return CommonResult.success(resultMap.entries.map {
            TimeDataVo(
                it.key,
                it.value
            )
        }.sortedBy { it.time })
    }


    @PostMapping("contrast12M36MOutUnderPerf")
    @Operation(description = "图表15")
    fun contrastOutUnderPerf(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<MutableList<Contrast12M36MOutUnderPerfVo>>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val (startTime, fundDataEndTime) =
            fundDepthResearchAttachService.findFundDepthResearchTimeRange(fundId) ?: return CommonResult.success(
                emptyList()
            )
        if (startTime == null || fundDataEndTime == null) {
            return CommonResult.success(
                emptyList(), message = "无数据"
            )
        }
        val fundBusinessDataMap =
            fundBusinessDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, fundDataEndTime)
                .groupBy({ it.time }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
        val resultMap =
            ConcurrentHashMap<LocalDate, MutableList<Contrast12M36MOutUnderPerfVo>>(fundBusinessDataMap.size * 2)

        val mEndTime = fundDataEndTime.plusMonths(36).atEndOfMonth()
        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            var index = 0L
            val contrastBusinessDataMap: Map<LocalDate, List<BigDecimal>> =
                contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(contrastId, startTime, fundDataEndTime)
                    .groupBy({ it.time!! }, { it.value.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP) })
            do {
                val currentMonth = startTime.plusMonths(index).atEndOfMonth()

                val twelveMOutUnderPerf = if (index < 11 || currentMonth > fundDataEndTime) {
                    null
                } else {
                    val fundMRollPerf = (0..11L).map { i ->
                        fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    val contrastMRollPerf = (0..11L).map { i ->
                        contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    fundMRollPerf.subtract(contrastMRollPerf)
                }
                val thirtySixMOutUnderPerf = if (index < 35) {
                    null
                } else {
                    val fundMRollPerf = (0..35L).map { i ->
                        fundBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    val contrastMRollPerf = (0..35L).map { i ->
                        contrastBusinessDataMap[currentMonth.minusMonths(i).atEndOfMonth()]?.first() ?: BigDecimal.ZERO
                    }.ytd() ?: BigDecimal.ZERO
                    fundMRollPerf.subtract(contrastMRollPerf)
                }
                index++
                val list = (resultMap[currentMonth] ?: mutableListOf())
                list.add(
                    Contrast12M36MOutUnderPerfVo(
                        contrastId,
                        contrast.name,
                        twelveMOutUnderPerf,
                        thirtySixMOutUnderPerf
                    )
                )
                resultMap[currentMonth] = list
            } while (!(currentMonth.year == mEndTime.year && currentMonth.month == mEndTime.month))
        }

        return CommonResult.success(resultMap.entries.map {
            TimeDataVo(
                it.key,
                it.value
            )
        }.sortedBy { it.time })
    }

    @PostMapping("netExposureCumulativePerformance")
    @Operation(description = "图表16")
    fun netExposureCumulativePerformance(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<NetExposureCumulativePerformanceVo>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val fundDepthResearchAttaches = fundDepthResearchAttachDao.findAllByFundIdIs(fundId)
        if (fundDepthResearchAttaches.isEmpty()) {
            return CommonResult.success(emptyList())
        }
        val endTime = fundDepthResearchAttaches.first().dataMonth
        val startTime = fundDepthResearchAttaches.last().dataMonth
        val allFundNavData =
            fundNavDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, endTime).groupBy { it.time!! }
        val resultMap =
            ConcurrentHashMap<LocalDate, TimeDataVo<NetExposureCumulativePerformanceVo>>(
                fundDepthResearchAttaches.size
            )
        for (fundDepthResearchAttach in fundDepthResearchAttaches) {
            resultMap[fundDepthResearchAttach.dataMonth] = TimeDataVo(
                fundDepthResearchAttach.dataMonth,
                NetExposureCumulativePerformanceVo(
                    fundDepthResearchAttach.exposureNet,
                    fundDepthResearchAttach.averageNet,
                    fundDepthResearchAttach.exposureNetLimit1,
                    fundDepthResearchAttach.exposureNetLimit2,
                    allFundNavData[fundDepthResearchAttach.dataMonth]?.first()?.value
                )
            )
        }
        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            val contrastAllBusinessData = contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                contrastId,
                startTime,
                endTime
            )
            for (contrastNavData in contrastService.calcContrastNavData(contrast, contrastAllBusinessData)) {
                resultMap[contrastNavData.time!!]?.value?.contrastNavInfos?.add(
                    ContrastValueVo(
                        contrastId,
                        contrast.name,
                        contrastNavData.value
                    )
                )
            }
        }
        val results = resultMap.values.sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    NetExposureCumulativePerformanceVo(
                        null,
                        first.value.averageNet,
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        first.value.contrastNavInfos.map {
                            ContrastValueVo(
                                it.contrastId,
                                it.contrastName,
                                BigDecimal.valueOf(100)
                            )
                        }.toMutableList()
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @PostMapping("grossExposureCumulativePerformance")
    @Operation(description = "图表17")
    fun grossExposureCumulativePerformance(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<GrossExposureCumulativePerformanceVo>>> {
        val fundId = batchQueryVo.baseFundId
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val fundDepthResearchAttaches = fundDepthResearchAttachDao.findAllByFundIdIs(fundId)
        if (fundDepthResearchAttaches.isEmpty()) {
            return CommonResult.success(emptyList())
        }
        val endTime = fundDepthResearchAttaches.first().dataMonth
        val startTime = fundDepthResearchAttaches.last().dataMonth
        val allFundNavData =
            fundNavDataDao.findAllByFundIdAndTimeBetween(fundId, startTime, endTime).groupBy { it.time!! }
        val resultMap =
            ConcurrentHashMap<LocalDate, TimeDataVo<GrossExposureCumulativePerformanceVo>>(
                fundDepthResearchAttaches.size
            )
        for (fundDepthResearchAttach in fundDepthResearchAttaches) {
            resultMap[fundDepthResearchAttach.dataMonth] = TimeDataVo(
                fundDepthResearchAttach.dataMonth,
                GrossExposureCumulativePerformanceVo(
                    fundDepthResearchAttach.exposureGross,
                    fundDepthResearchAttach.averageGross,
                    fundDepthResearchAttach.exposureGrossLimit1,
                    fundDepthResearchAttach.exposureGrossLimit2,
                    allFundNavData[fundDepthResearchAttach.dataMonth]?.first()?.value
                )
            )
        }
        batchQueryVo.contrastIds.consumeAsyncBlocking { contrastId ->
            val contrast = contrastDao.findByIdOrNull(contrastId) ?: return@consumeAsyncBlocking
            val contrastAllBusinessData = contrastBusinessDataDao.findAllByContrastIdAndTimeBetween(
                contrastId,
                startTime,
                endTime
            )
            for (contrastNavData in contrastService.calcContrastNavData(contrast, contrastAllBusinessData)) {
                resultMap[contrastNavData.time!!]?.value?.contrastNavInfos?.add(
                    ContrastValueVo(
                        contrastId,
                        contrast.name,
                        contrastNavData.value
                    )
                )
            }
        }
        val results = resultMap.values.sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    GrossExposureCumulativePerformanceVo(
                        null,
                        first.value.averageGross,
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        first.value.contrastNavInfos.map {
                            ContrastValueVo(
                                it.contrastId,
                                it.contrastName,
                                BigDecimal.valueOf(100)
                            )
                        }.toMutableList()
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @GetMapping("longShortExposureCumulativePerformance")
    @Operation(description = "图表18-19")
    fun longShortExposureCumulativePerformance(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<LongShortExposureCumulativePerformanceVo>>> {
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { fundDepthResearchAttach ->
            TimeDataVo(
                fundDepthResearchAttach.dataMonth,
                LongShortExposureCumulativePerformanceVo(
                    fundDepthResearchAttach.exposureLong,
                    fundDepthResearchAttach.exposureShort,
                    fundDepthResearchAttach.averageLong,
                    fundDepthResearchAttach.averageShort,
                    fundDepthResearchAttach.longCumulative,
                    fundDepthResearchAttach.shortCumulative,
                )
            )
        }.sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    LongShortExposureCumulativePerformanceVo(
                        null,
                        null,
                        first.value.averageLong,
                        first.value.averageShort,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100),
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @GetMapping("longShortExposureByAssetClass")
    @Operation(description = "图表20")
    fun longShortExposureByAssetClass(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<LongShortExposureByAssetClassVo>>> {
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { fundDepthResearchAttach ->
            TimeDataVo(
                fundDepthResearchAttach.dataMonth,
                LongShortExposureByAssetClassVo(
                    fundDepthResearchAttach.exposureEquitiesL,
                    fundDepthResearchAttach.exposureFixedIncomeL,
                    fundDepthResearchAttach.exposureCurrenciesFXL,
                    fundDepthResearchAttach.exposureCommoditiesL,
                    fundDepthResearchAttach.exposureRealEstateAssetClassL,
                    fundDepthResearchAttach.exposureOthersAssetClassL,
                    fundDepthResearchAttach.exposureAlternativeInvestmentsL,
                    fundDepthResearchAttach.exposureEquitiesS,
                    fundDepthResearchAttach.exposureFixedIncomeS,
                    fundDepthResearchAttach.exposureCurrenciesFXS,
                    fundDepthResearchAttach.exposureCommoditiesS,
                    fundDepthResearchAttach.exposureRealEstateAssetClassS,
                    fundDepthResearchAttach.exposureOtherAssetClassS,
                    fundDepthResearchAttach.exposureAlternativeInvestmentsS,
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(result)
    }

    @GetMapping("shortExposureCumulativePerformance")
    @Operation(description = "图表21")
    fun shortExposureCumulativePerformance(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<ShortExposureCumulativePerformanceVo>>> {
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { fundDepthResearchAttach ->
            TimeDataVo(
                fundDepthResearchAttach.dataMonth,
                ShortExposureCumulativePerformanceVo(
                    fundDepthResearchAttach.exposureShort,
                    fundDepthResearchAttach.averageShort,
                    fundDepthResearchAttach.shortCumulative,
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(result)
    }

    @GetMapping("exposureInfo")
    @Operation(description = "图表22-图表33")
    fun exposureInfo(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<ExposureInfo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId)
            .map { TimeDataVo(it.dataMonth, (it.exposureInfo ?: ExposureInfo())) }
            .sortedBy { it.time }
        return CommonResult.success(result)
    }

    @GetMapping("concentrationOfTopPositionsOnw")
    @Operation(description = "图表34-35")
    fun concentrationOfTopPositionsOne(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<ConcentrationOfTopPositionsOneVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map {
            TimeDataVo(
                it.dataMonth,
                ConcentrationOfTopPositionsOneVo(
                    it.top1LongPositionExposure,
                    it.top1ShortPositionExposure,
                    it.exposureTop5Longs,
                    it.exposureTop5Shorts,
                    it.exposureTop10Longs,
                    it.exposureTop10Shorts,
                    it.exposureLongLimit,
                    it.exposureShortLimit,
                    it.exposureTop5LongLimit,
                    it.exposureTop5ShortLimit,
                    it.exposureTop10LongLimit,
                    it.exposureTop10ShortLimit,
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(result)
    }

    @GetMapping("concentrationOfTopPositions")
    @Operation(description = "图表36-38")
    fun concentrationOfTopPositionsTwo(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<ConcentrationOfTopPositionsTwoVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map {
            TimeDataVo(
                it.dataMonth,
                ConcentrationOfTopPositionsTwoVo(
                    it.top1LongPositionExposure,
                    it.top1ShortPositionExposure,
                    it.exposureLongLimit,
                    it.exposureShortLimit,
                    it.topOneLongDivideTotal,
                    it.topOneShortDivideTotal,
                    it.topFiveLongDivideTotal,
                    it.topFiveShortDivideTotal,
                    it.topTenLongDivideTotal,
                    it.topTenShortDivideTotal,
                    it.exposureTop10Longs,
                    it.exposureTop10Shorts,
                    it.exposureTop10LongLimit,
                    it.exposureTop10ShortLimit,
                    it.exposureTop5Longs,
                    it.exposureTop5Shorts,
                    it.exposureTop5LongLimit,
                    it.exposureTop5ShortLimit
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(result)
    }


    @GetMapping("numberOfPositionsGrossExposure")
    @Operation(description = "图表39")
    fun numberOfPositionsGrossExposure(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<NumberOfPositionsGrossExposureVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map {
            TimeDataVo(
                it.dataMonth,
                NumberOfPositionsGrossExposureVo(
                    it.ofLong,
                    it.ofShort,
                    it.exposureLong,
                    it.exposureShort,
                )
            )
        }.sortedBy { it.time }
        return CommonResult.success(result)
    }

    @GetMapping("largestLongPositions")
    @Operation(description = "图表40")
    fun largestLongPositions(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<LargestLongPositionsVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map {
            TimeDataVo(
                it.dataMonth,
                LargestLongPositionsVo(
                    it.top1LongPositionName,
                    it.top1LongPositionExposure,
                    it.top2LongPositionName,
                    it.top2LongPositionExposure,
                    it.top3LongPositionName,
                    it.top3LongPositionExposure,
                    it.top4LongPositionName,
                    it.top4LongPositionExposure,
                    it.top5LongPositionName,
                    it.top5LongPositionExposure,
                    it.top6LongPositionName,
                    it.top6LongPositionExposure,
                    it.top7LongPositionName,
                    it.top7LongPositionExposure,
                    it.top8LongPositionName,
                    it.top8LongPositionExposure,
                    it.top9LongPositionName,
                    it.top9LongPositionExposure,
                    it.top10LongPositionName,
                    it.top10LongPositionExposure,
                )
            )
        }.sortedByDescending { it.time }
        return CommonResult.success(result)
    }

    @GetMapping("largestShortPositions")
    @Operation(description = "图表41")
    fun largestShortPositions(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<LargestShortPositionsVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val result = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map {
            TimeDataVo(
                it.dataMonth,
                LargestShortPositionsVo(
                    it.top1ShortPositionName,
                    it.top1ShortPositionExposure,
                    it.top2ShortPositionName,
                    it.top2ShortPositionExposure,
                    it.top3ShortPositionName,
                    it.top3ShortPositionExposure,
                    it.top4ShortPositionName,
                    it.top4ShortPositionExposure,
                    it.top5ShortPositionName,
                    it.top5ShortPositionExposure,
                    it.top6ShortPositionName,
                    it.top6ShortPositionExposure,
                    it.top7ShortPositionName,
                    it.top7ShortPositionExposure,
                    it.top8ShortPositionName,
                    it.top8ShortPositionExposure,
                    it.top9ShortPositionName,
                    it.top9ShortPositionExposure,
                    it.top10ShortPositionName,
                    it.top10ShortPositionExposure,
                )
            )
        }.sortedByDescending { it.time }
        return CommonResult.success(result)
    }

    @PostMapping("overallBookAnnualAlphaBeta")
    @Operation(description = "图表 42")
    fun overallBookAnnualAlphaBeta(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<OverallBookAnnualAlphaBetaVo>>> {
        val fundId = batchQueryVo.baseFundId
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = batchQueryVo.contrastIds.mapAsyncBlocking { contrastId ->
            fundDepthResearchContrastAttachDao.findAllByFundIdIsAndContrastIdIs(fundId, contrastId)
                .map { depthResearchContrastAttach ->
                    TimeDataVo(
                        depthResearchContrastAttach.dataMonth!!,
                        OverallBookAnnualAlphaBetaVo(
                            alphaCumulative = depthResearchContrastAttach.alphaCumulative,
                            betaCumulative = depthResearchContrastAttach.betaCumulative,
                        )
                    )
                }.groupBy { it.time.year }
                .map { (year, list) ->
                    list.sortedBy { it.time }
                    fundDepthResearchContrastAttachAnnualInfoDao.findByFundIdIsAndContrastIdIsAndYearIs(
                        fundId,
                        contrastId,
                        year
                    )?.let { fundDepthResearchContrastAttachAnnualInfo ->
                        list.last().value.alphaAnnual = fundDepthResearchContrastAttachAnnualInfo.alphaAnnual
                        list.last().value.betaAnnual = fundDepthResearchContrastAttachAnnualInfo.betaAnnual
                    }
                    list
                }.flatten().sortedBy { it.time }
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    OverallBookAnnualAlphaBetaVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @PostMapping("overallBookQuarterlyAlphaBeta")
    @Operation(description = "图表 43")
    fun overallBookQuarterlyAlphaBeta(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<OverallBookQuarterlyAlphaBetaVo>>> {
        val fundId = batchQueryVo.baseFundId
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = batchQueryVo.contrastIds.mapAsyncBlocking { contrastId ->
            fundDepthResearchContrastAttachDao.findAllByFundIdIsAndContrastIdIs(fundId, contrastId)
                .map { depthResearchContrastAttach ->
                    TimeDataVo(
                        depthResearchContrastAttach.dataMonth!!,
                        OverallBookQuarterlyAlphaBetaVo(
                            alphaCumulative = depthResearchContrastAttach.alphaCumulative,
                            betaCumulative = depthResearchContrastAttach.betaCumulative,
                        )
                    )
                }.groupBy { it.time.year }
                .map { (year, list) ->
                    val quarterMap = list.groupBy { it.time.quarter() }
                    fundDepthResearchContrastAttachQuarterlyInfoDao.findAllByFundIdIsAndContrastIdIsAndYearIs(
                        fundId,
                        contrastId,
                        year
                    ).forEach { fundDepthResearchContrastAttachQuarterlyInfo ->
                        quarterMap[fundDepthResearchContrastAttachQuarterlyInfo.dataMonth.quarter()]?.maxByOrNull { it.time }?.value?.let {
                            it.alphaQuarterly = fundDepthResearchContrastAttachQuarterlyInfo.alphaQuarterly
                            it.betaQuarterly = fundDepthResearchContrastAttachQuarterlyInfo.betaQuarterly
                        }
                    }
                    list
                }.flatten().sortedBy { it.time }
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    OverallBookQuarterlyAlphaBetaVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @GetMapping("overallBookAnnualLongShort")
    @Operation(description = "图表 44")
    fun overallBookAnnualLongShort(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<OverallBookAnnualLongShortVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { depthResearchAttach ->
            TimeDataVo(
                depthResearchAttach.dataMonth,
                OverallBookAnnualLongShortVo(
                    longCumulative = depthResearchAttach.longCumulative,
                    shortCumulative = depthResearchAttach.shortCumulative,
                )
            )
        }.groupBy { it.time.year }.mapAsyncBlocking { (year, list) ->
            val orderedList = list.sortedBy { it.time }
            fundDepthResearchAttachAnnualInfoDao.findByFundIdIsAndYearIs(
                fundId,
                year
            )?.let { fundDepthResearchAttachAnnualInfo ->
                orderedList.last().value.longAnnual = fundDepthResearchAttachAnnualInfo.longAnnual
                orderedList.last().value.shortAnnual = fundDepthResearchAttachAnnualInfo.shortAnnual
            }
            orderedList
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    OverallBookAnnualLongShortVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @GetMapping("overallBookQuarterlyLongShort")
    @Operation(description = "图表 45")
    fun overallBookQuarterlyLongShort(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<OverallBookQuarterlyLongShortVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { depthResearchAttach ->
            TimeDataVo(
                depthResearchAttach.dataMonth,
                OverallBookQuarterlyLongShortVo(
                    longCumulative = depthResearchAttach.longCumulative,
                    shortCumulative = depthResearchAttach.shortCumulative,
                )
            )
        }.groupBy { it.time.year }.mapAsyncBlocking { (year, timeDataVos) ->
            val quarterMap = timeDataVos.groupBy { it.time.quarter() }
            fundDepthResearchAttachQuarterlyInfoDao.findByFundIdIsAndYearIs(
                fundId,
                year
            ).forEach { fundDepthResearchAttachQuarterlyInfo ->
                quarterMap[fundDepthResearchAttachQuarterlyInfo.dataMonth.quarter()]?.maxByOrNull { it.time }?.value?.let {
                    it.longQuarterly = fundDepthResearchAttachQuarterlyInfo.longQuarterly
                    it.shortQuarterly = fundDepthResearchAttachQuarterlyInfo.shortQuarterly
                }
            }
            timeDataVos
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    OverallBookQuarterlyLongShortVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @PostMapping("longBookAnnualAlphaBeta")
    @Operation(description = "图表 46")
    fun longBookAnnualAlphaBeta(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<LongBookAnnualAlphaBetaVo>>> {
        val fundId = batchQueryVo.baseFundId
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = batchQueryVo.contrastIds.mapAsyncBlocking { contrastId ->
            fundDepthResearchContrastAttachDao.findAllByFundIdIsAndContrastIdIs(fundId, contrastId)
                .map { depthResearchContrastAttach ->
                    TimeDataVo(
                        depthResearchContrastAttach.dataMonth!!,
                        LongBookAnnualAlphaBetaVo(
                            alphaLongCumulative = depthResearchContrastAttach.alphaLongCumulative,
                            betaLongCumulative = depthResearchContrastAttach.betaLongCumulative,
                        )
                    )
                }.groupBy { it.time.year }.map { (year, list) ->
                    val orderedList = list.sortedBy { it.time }
                    fundDepthResearchContrastAttachAnnualInfoDao.findByFundIdIsAndContrastIdIsAndYearIs(
                        fundId,
                        contrastId,
                        year
                    )?.let { fundDepthResearchContrastAttachAnnualInfo ->
                        orderedList.last().value.alphaLongAnnual =
                            fundDepthResearchContrastAttachAnnualInfo.alphaLongAnnual
                        orderedList.last().value.betaLongAnnual =
                            fundDepthResearchContrastAttachAnnualInfo.betaLongAnnual
                    }
                    orderedList
                }.flatten().sortedBy { it.time }
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    LongBookAnnualAlphaBetaVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @PostMapping("longBookQuarterlyAlphaBeta")
    @Operation(description = "图表 47")
    fun longBookQuarterlyAlphaBeta(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<LongBookQuarterlyAlphaBetaVo>>> {
        val fundId = batchQueryVo.baseFundId
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = batchQueryVo.contrastIds.mapAsyncBlocking { contrastId ->
            fundDepthResearchContrastAttachDao.findAllByFundIdIsAndContrastIdIs(fundId, contrastId)
                .map { depthResearchContrastAttach ->
                    TimeDataVo(
                        depthResearchContrastAttach.dataMonth!!,
                        LongBookQuarterlyAlphaBetaVo(
                            alphaLongCumulative = depthResearchContrastAttach.alphaLongCumulative,
                            betaLongCumulative = depthResearchContrastAttach.betaLongCumulative,
                        )
                    )
                }.groupBy { it.time.year }
                .map { (year, list) ->
                    val quarterMap = list.groupBy { it.time.quarter() }
                    fundDepthResearchContrastAttachQuarterlyInfoDao.findAllByFundIdIsAndContrastIdIsAndYearIs(
                        fundId,
                        contrastId,
                        year
                    ).forEach { fundDepthResearchContrastAttachQuarterlyInfo ->
                        quarterMap[fundDepthResearchContrastAttachQuarterlyInfo.dataMonth.quarter()]?.maxByOrNull { it.time }?.value?.let {
                            it.alphaLongQuarterly = fundDepthResearchContrastAttachQuarterlyInfo.alphaLongQuarterly
                            it.betaLongQuarterly = fundDepthResearchContrastAttachQuarterlyInfo.betaLongQuarterly
                        }
                    }
                    list
                }.flatten().sortedBy { it.time }
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    LongBookQuarterlyAlphaBetaVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @PostMapping("shortBookAnnualAlphaBeta")
    @Operation(description = "图表 48")
    fun shortBookAnnualAlphaBeta(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<ShortBookAnnualAlphaBetaVo>>> {
        val fundId = batchQueryVo.baseFundId
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = batchQueryVo.contrastIds.mapAsyncBlocking { contrastId ->
            fundDepthResearchContrastAttachDao.findAllByFundIdIsAndContrastIdIs(fundId, contrastId)
                .map { depthResearchContrastAttach ->
                    TimeDataVo(
                        depthResearchContrastAttach.dataMonth!!,
                        ShortBookAnnualAlphaBetaVo(
                            alphaShortCumulative = depthResearchContrastAttach.alphaShortCumulative,
                            betaShortCumulative = depthResearchContrastAttach.betaShortCumulative,
                        )
                    )
                }.groupBy { it.time.year }.map { (year, list) ->
                    val orderedList = list.sortedBy { it.time }
                    fundDepthResearchContrastAttachAnnualInfoDao.findByFundIdIsAndContrastIdIsAndYearIs(
                        fundId,
                        contrastId,
                        year
                    )?.let { fundDepthResearchContrastAttachAnnualInfo ->
                        orderedList.last().value.alphaShortAnnual =
                            fundDepthResearchContrastAttachAnnualInfo.alphaShortAnnual
                        orderedList.last().value.betaShortAnnual =
                            fundDepthResearchContrastAttachAnnualInfo.betaShortAnnual
                    }
                    orderedList
                }.flatten().sortedBy { it.time }
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    ShortBookAnnualAlphaBetaVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @PostMapping("shortBookQuarterlyAlphaBeta")
    @Operation(description = "图表 49")
    fun shortBookQuarterlyAlphaBeta(@RequestBody batchQueryVo: BatchFundContrastDataQueryVo): CommonResult<List<TimeDataVo<ShortBookQuarterlyAlphaBetaVo>>> {
        val fundId = batchQueryVo.baseFundId
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results = batchQueryVo.contrastIds.mapAsyncBlocking { contrastId ->
            fundDepthResearchContrastAttachDao.findAllByFundIdIsAndContrastIdIs(fundId, contrastId)
                .map { depthResearchContrastAttach ->
                    TimeDataVo(
                        depthResearchContrastAttach.dataMonth!!,
                        ShortBookQuarterlyAlphaBetaVo(
                            alphaShortCumulative = depthResearchContrastAttach.alphaShortCumulative,
                            betaShortCumulative = depthResearchContrastAttach.betaShortCumulative,
                        )
                    )
                }.groupBy { it.time.year }
                .map { (year, list) ->
                    val quarterMap = list.groupBy { it.time.quarter() }
                    fundDepthResearchContrastAttachQuarterlyInfoDao.findAllByFundIdIsAndContrastIdIsAndYearIs(
                        fundId,
                        contrastId,
                        year
                    ).forEach { fundDepthResearchContrastAttachQuarterlyInfo ->
                        quarterMap[fundDepthResearchContrastAttachQuarterlyInfo.dataMonth.quarter()]?.maxByOrNull { it.time }?.value?.let {
                            it.alphaShortQuarterly = fundDepthResearchContrastAttachQuarterlyInfo.alphaShortQuarterly
                            it.betaShortQuarterly = fundDepthResearchContrastAttachQuarterlyInfo.betaShortQuarterly
                        }
                    }
                    list
                }.flatten().sortedBy { it.time }
        }.flatten().sortedBy { it.time }.toMutableList()
        if (results.isNotEmpty()) {
            val first = results.first()
            results.addFirst(
                TimeDataVo(
                    first.time.minusMonths(1L).atEndOfMonth(),
                    ShortBookQuarterlyAlphaBetaVo(
                        null,
                        null,
                        BigDecimal.valueOf(100),
                        BigDecimal.valueOf(100)
                    )
                )
            )
        }
        return CommonResult.success(results)
    }

    @GetMapping("annualPnLAttributionByCountry")
    @Operation(description = "图表 50")
    fun annualPnLAttributionByCountry(@RequestParam fundId: Int): CommonResult<List<IntTimeDataVo<AnnualPnLAttributionByCountryVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results =
            fundDepthResearchAttachAnnualInfoDao.findAllByFundIdIs(fundId).map { depthResearchAttachAnnualInfo ->
                IntTimeDataVo(
                    depthResearchAttachAnnualInfo.year!!,
                    AnnualPnLAttributionByCountryVo(
                        depthResearchAttachAnnualInfo.uSAnnual,
                        depthResearchAttachAnnualInfo.canadaAnnual,
                        depthResearchAttachAnnualInfo.brazilAnnual,
                        depthResearchAttachAnnualInfo.mexicoAnnual,
                        depthResearchAttachAnnualInfo.argentinaAnnual,
                        depthResearchAttachAnnualInfo.chileAnnual,
                        depthResearchAttachAnnualInfo.colombiaAnnual,
                        depthResearchAttachAnnualInfo.peruAnnual,
                        depthResearchAttachAnnualInfo.uKAnnual,
                        depthResearchAttachAnnualInfo.germanyAnnual,
                        depthResearchAttachAnnualInfo.franceAnnual,
                        depthResearchAttachAnnualInfo.switzerlandAnnual,
                        depthResearchAttachAnnualInfo.netherlandsAnnual,
                        depthResearchAttachAnnualInfo.italyAnnual,
                        depthResearchAttachAnnualInfo.spainAnnual,
                        depthResearchAttachAnnualInfo.swedenAnnual,
                        depthResearchAttachAnnualInfo.saudiArabiaAnnual,
                        depthResearchAttachAnnualInfo.unitedArabEmiratesAnnual,
                        depthResearchAttachAnnualInfo.southAfricaAnnual,
                        depthResearchAttachAnnualInfo.nigeriaAnnual,
                        depthResearchAttachAnnualInfo.egyptAnnual,
                        depthResearchAttachAnnualInfo.japanAnnual,
                        depthResearchAttachAnnualInfo.chinaAnnual,
                        depthResearchAttachAnnualInfo.hongKongAnnual,
                        depthResearchAttachAnnualInfo.taiwanAnnual,
                        depthResearchAttachAnnualInfo.australiaAnnual,
                        depthResearchAttachAnnualInfo.indiaAnnual,
                        depthResearchAttachAnnualInfo.southKoreaAnnual,
                        depthResearchAttachAnnualInfo.singaporeAnnual,
                        depthResearchAttachAnnualInfo.indonesiaAnnual,
                        depthResearchAttachAnnualInfo.malaysiaAnnual,
                        depthResearchAttachAnnualInfo.thailandAnnual,
                        depthResearchAttachAnnualInfo.philippinesAnnual,
                        depthResearchAttachAnnualInfo.vietnamAnnual,
                        depthResearchAttachAnnualInfo.newZealandAnnual,
                        depthResearchAttachAnnualInfo.otherCountryAnnual,
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("cumulativePnLAttributionByCountry")
    @Operation(description = "图表 51")
    fun cumulativePnLAttributionByCountry(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<CumulativePnLAttributionByCountryVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results =
            fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { fundDepthResearchAttach ->
                TimeDataVo(
                    fundDepthResearchAttach.dataMonth,
                    CumulativePnLAttributionByCountryVo(
                        fundDepthResearchAttach.cumulativeInfo?.uSCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.canadaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.brazilCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.mexicoCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.argentinaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.chileCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.colombiaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.peruCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.uKCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.germanyCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.franceCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.switzerlandCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.netherlandsCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.italyCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.spainCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.swedenCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.saudiArabiaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.unitedArabEmiratesCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.southAfricaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.nigeriaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.egyptCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.japanCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.chinaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.hongKongCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.taiwanCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.australiaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.indiaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.southKoreaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.singaporeCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.indonesiaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.malaysiaCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.thailandCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.philippinesCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.vietnamCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.newZealandCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.otherCountryCumulative
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("annualPnLAttributionBySector")
    @Operation(description = "图表 52")
    fun annualPnLAttributionBySector(@RequestParam fundId: Int): CommonResult<List<IntTimeDataVo<AnnualPnLAttributionBySectorVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results =
            fundDepthResearchAttachAnnualInfoDao.findAllByFundIdIs(fundId).map { depthResearchAttachAnnualInfo ->
                IntTimeDataVo(
                    depthResearchAttachAnnualInfo.year!!,
                    AnnualPnLAttributionBySectorVo(
                        depthResearchAttachAnnualInfo.consumerStaplesAnnual,
                        depthResearchAttachAnnualInfo.consumerDiscretionaryAnnual,
                        depthResearchAttachAnnualInfo.informationTechnologyAnnual,
                        depthResearchAttachAnnualInfo.financialsAnnual,
                        depthResearchAttachAnnualInfo.healthCareAnnual,
                        depthResearchAttachAnnualInfo.industrialsAnnual,
                        depthResearchAttachAnnualInfo.utilitiesAnnual,
                        depthResearchAttachAnnualInfo.materialsAnnual,
                        depthResearchAttachAnnualInfo.realEstateAnnual,
                        depthResearchAttachAnnualInfo.energyAnnual,
                        depthResearchAttachAnnualInfo.communicationServiceAnnual,
                        depthResearchAttachAnnualInfo.indexETFAnnual,
                        depthResearchAttachAnnualInfo.cashAnnual,
                        depthResearchAttachAnnualInfo.othersAnnual,
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("cumulativePnLAttributionBySector")
    @Operation(description = "图表 53")
    fun cumulativePnLAttributionBySector(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<CumulativePnLAttributionBySectorVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results =
            fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { fundDepthResearchAttach ->
                TimeDataVo(
                    fundDepthResearchAttach.dataMonth,
                    CumulativePnLAttributionBySectorVo(
                        fundDepthResearchAttach.cumulativeInfo?.consumerStaplesCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.consumerDiscretionaryCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.informationTechnologyCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.financialsCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.healthCareCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.industrialsCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.utilitiesCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.materialsCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.realEstateCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.energyCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.communicationServiceCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.indexETFCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.cashCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.othersCumulative
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("annualPnLAttributionByMarketCap")
    @Operation(description = "图表 54")
    fun annualPnLAttributionByMarketCap(@RequestParam fundId: Int): CommonResult<List<IntTimeDataVo<AnnualPnLAttributionByMarketCapVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results =
            fundDepthResearchAttachAnnualInfoDao.findAllByFundIdIs(fundId).map { depthResearchAttachAnnualInfo ->
                IntTimeDataVo(
                    depthResearchAttachAnnualInfo.year!!,
                    AnnualPnLAttributionByMarketCapVo(
                        depthResearchAttachAnnualInfo.bigger10bnAnnual,
                        depthResearchAttachAnnualInfo.fiveTo10bnAnnual,
                        depthResearchAttachAnnualInfo.oneTo5bnAnnual,
                        depthResearchAttachAnnualInfo.zeroPointThreeToOneAnnual,
                        depthResearchAttachAnnualInfo.litterZeroPointThreebnAnnual,
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("cumulativePnLAttributionByMarketCap")
    @Operation(description = "图表 55")
    fun cumulativePnLAttributionByMarketCap(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<CumulativePnLAttributionByMarketCapVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results =
            fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { fundDepthResearchAttach ->
                TimeDataVo(
                    fundDepthResearchAttach.dataMonth,
                    CumulativePnLAttributionByMarketCapVo(
                        fundDepthResearchAttach.cumulativeInfo?.bigger10bnCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.fiveTo10bnCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.oneTo5bnCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.zeroPointThreeToOnebnCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.litterZeroPointThreebnCumulative,
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("annualPnLAttributionByAssetClass")
    @Operation(description = "图表 56")
    fun annualPnLAttributionByAssetClass(@RequestParam fundId: Int): CommonResult<List<IntTimeDataVo<AnnualPnLAttributionByAssetClassVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }
        val results =
            fundDepthResearchAttachAnnualInfoDao.findAllByFundIdIs(fundId).map { depthResearchAttachAnnualInfo ->
                IntTimeDataVo(
                    depthResearchAttachAnnualInfo.year!!,
                    AnnualPnLAttributionByAssetClassVo(
                        depthResearchAttachAnnualInfo.equitiesAnnual,
                        depthResearchAttachAnnualInfo.fixedIncomeAnnual,
                        depthResearchAttachAnnualInfo.currenciesFXAnnual,
                        depthResearchAttachAnnualInfo.commoditiesAnnual,
                        depthResearchAttachAnnualInfo.alternativeInvestmentsAnnual,
                        depthResearchAttachAnnualInfo.realEstateAssetClassAnnual,
                        depthResearchAttachAnnualInfo.otherAssetClassAnnual,
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("cumulativePnLAttributionByAssetClass")
    @Operation(description = "图表 57")
    fun cumulativePnLAttributionByAssetClass(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<CumulativePnLAttributionByAssetClassVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }

        val results =
            fundDepthResearchAttachDao.findAllByFundIdIs(fundId).map { fundDepthResearchAttach ->
                TimeDataVo(
                    fundDepthResearchAttach.dataMonth,
                    CumulativePnLAttributionByAssetClassVo(
                        fundDepthResearchAttach.cumulativeInfo?.equitiesCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.fixedIncomeCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.currenciesFXCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.commoditiesCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.alternativeInvestmentsCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.realEstateAssetClassCumulative,
                        fundDepthResearchAttach.cumulativeInfo?.otherAssetClassCumulative
                    )
                )
            }.sortedBy { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("topMonthlyPnlDrivers")
    @Operation(description = "图表 58")
    fun topMonthlyPnlDrivers(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<TopAnnualPnlDriversVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }

        val endYear = LocalDate.now().let {
            if (it.monthValue != 12) {
                return@let it.year - 1
            }
            return@let it.year
        }
        val results = (0..3).map { yearDiff ->
            val currentYear = endYear - yearDiff
            val fundDepthResearchAttach =
                fundDepthResearchAttachDao.findFundYearLastMonthData(fundId, currentYear)
            TimeDataVo(
                LocalDate.of(currentYear, 12, 31),
                TopAnnualPnlDriversVo(
                    fundDepthResearchAttach?.annualWinner1Name,
                    fundDepthResearchAttach?.annualWinner1Return,
                    fundDepthResearchAttach?.annualWinner2Name,
                    fundDepthResearchAttach?.annualWinner2Return,
                    fundDepthResearchAttach?.annualWinner3Name,
                    fundDepthResearchAttach?.annualWinner3Return,
                    fundDepthResearchAttach?.annualWinner4Name,
                    fundDepthResearchAttach?.annualWinner4Return,
                    fundDepthResearchAttach?.annualWinner5Name,
                    fundDepthResearchAttach?.annualWinner5Return,
                    fundDepthResearchAttach?.annualWinner6Name,
                    fundDepthResearchAttach?.annualWinner6Return,
                    fundDepthResearchAttach?.annualWinner7Name,
                    fundDepthResearchAttach?.annualWinner7Return,
                    fundDepthResearchAttach?.annualWinner8Name,
                    fundDepthResearchAttach?.annualWinner8Return,
                    fundDepthResearchAttach?.annualWinner9Name,
                    fundDepthResearchAttach?.annualWinner9Return,
                    fundDepthResearchAttach?.annualWinner10Name,
                    fundDepthResearchAttach?.annualWinner10Return,
                    fundDepthResearchAttach?.annualLoser1Name,
                    fundDepthResearchAttach?.annualLoser1Return,
                    fundDepthResearchAttach?.annualLoser2Name,
                    fundDepthResearchAttach?.annualLoser2Return,
                    fundDepthResearchAttach?.annualLoser3Name,
                    fundDepthResearchAttach?.annualLoser3Return,
                    fundDepthResearchAttach?.annualLoser4Name,
                    fundDepthResearchAttach?.annualLoser4Return,
                    fundDepthResearchAttach?.annualLoser5Name,
                    fundDepthResearchAttach?.annualLoser5Return,
                    fundDepthResearchAttach?.annualLoser6Name,
                    fundDepthResearchAttach?.annualLoser6Return,
                    fundDepthResearchAttach?.annualLoser7Name,
                    fundDepthResearchAttach?.annualLoser7Return,
                    fundDepthResearchAttach?.annualLoser8Name,
                    fundDepthResearchAttach?.annualLoser8Return,
                    fundDepthResearchAttach?.annualLoser9Name,
                    fundDepthResearchAttach?.annualLoser9Return,
                    fundDepthResearchAttach?.annualLoser10Name,
                    fundDepthResearchAttach?.annualLoser10Return
                )
            )
        }.sortedByDescending { it.time }
        return CommonResult.success(results)
    }


    @GetMapping("topAnnualMonthlyPnlWinners")
    @Operation(description = "图表 59")
    fun topAnnualMonthlyPnlWinners(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<TopMonthlyPnlWinnersVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }

        // 统一使用每个月最后一天
        val endDate = LocalDate.now().atEndOfMonth()
        val dataMonthValueMap =
            fundDepthResearchAttachDao.findAllByFundIdAndDataMonthBetween(fundId, endDate.minusMonths(11), endDate)
                .groupBy { it.dataMonth }
        val results = (0L..11L).map { i ->
            val currentMonth = endDate.minusMonths(i).atEndOfMonth()
            TimeDataVo(
                currentMonth,
                TopMonthlyPnlWinnersVo(
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner1Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner1Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner2Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner2Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner3Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner3Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner4Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner4Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner5Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner5Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner6Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner6Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner7Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner7Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner8Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner8Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner9Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner9Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner10Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyWinner10Return
                )
            )
        }.sortedByDescending { it.time }
        return CommonResult.success(results)
    }

    @GetMapping("topAnnualMonthlyPnlLosers")
    @Operation(description = "图表 60")
    fun topAnnualMonthlyPnlLosers(@RequestParam fundId: Int): CommonResult<List<TimeDataVo<TopMonthlyPnlLosersVo>>> {
        fundDao.findById(fundId).orElseThrow { SystemException("未找到基金信息") }

        val endDate = LocalDate.now().atEndOfMonth()
        val dataMonthValueMap =
            fundDepthResearchAttachDao.findAllByFundIdAndDataMonthBetween(fundId, endDate.minusMonths(11), endDate)
                .groupBy { it.dataMonth }

        val results = (0L..11L).map { i ->
            val currentMonth = endDate.minusMonths(i).atEndOfMonth()
            TimeDataVo(
                currentMonth,
                TopMonthlyPnlLosersVo(
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser1Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser1Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser2Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser2Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser3Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser3Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser4Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser4Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser5Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser5Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser6Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser6Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser7Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser7Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser8Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser8Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser9Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser9Return,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser10Name,
                    dataMonthValueMap[currentMonth]?.first()?.monthlyLoser10Return
                )
            )
        }.sortedByDescending { it.time }
        return CommonResult.success(results)
    }
}